package merlin.servlets;

import java.io.*;
import java.util.*;
import java.text.*;

import javax.servlet.*;
import javax.servlet.http.*;

import merlin.beans.*;
import merlin.dao.*;
import merlin.managers.*;
import merlin.servlets.MerlinServlet.ReportRefresher;
import merlin.utils.*;


/**
 * Front contoller for the industry Watch web application.
 */
public class IndustryWatchServlet extends HttpServlet{

	// constants to represent the views of the IW application.
	public static final int VIEW_INDUSTRY_SECTORS 	= 0;
	public static final int VIEW_INDUSTRY_AVERAGES 	= 1;
	public static final int VIEW_INDUSTRY_ANALYSIS 	= 2;

	public static final String strFinancialSeries	= "IAD_SALES_GROWTH,IAD_OPERATING_PROFIT_GROWTH,IAD_GROSS_PROFIT_MARGIN,IAD_OPERATING_PROFIT_MARGIN,IAD_PRE_TAX_PROFIT_MARGIN,IAD_RETURN_ON_TOTAL_ASSETS,IAD_RETURN_ON_EQUITY,IAD_SALES_PER_EMPLOYEE,IAD_WAGE_PER_EMPLOYEE,IAD_CURRENT_RATIO,IAD_QUICK_RATIO,IAD_DEBT_RATIO,IAD_AVERAGE_CREDIT_GIVEN,IAD_AVERAGE_CREDIT_TAKEN,IAD_STOCK_TURNOVER,IAD_FIXED_ASSET_TURNOVER,IAD_DEBTOR_TURNOVER";
	public static final String strDataSeries		= ",_BC,_SC,_BP";

	ReportRefresher reportRefresher ;	// Thread to refresh the report titles.

	boolean titlesLoaded = false;	//one-time operation.

	/**
	 * @param cfg	servlet config.
	 */
	public void init(ServletConfig cfg)throws ServletException{
		try{
			super.init(cfg);

			ApplicationResources resource = ApplicationResources.getInstance();
			// Launch the report titles refresher thread.
			int refreshInterval = 24;	// default every 24 hours.
			try{
				String str = resource.getProperty("report.refresh.interval");
	        	if(str != null)refreshInterval = Integer.parseInt(str);
			}catch(Exception e){
				System.out.println(e);
			}
			//*
			reportRefresher = new ReportRefresher(cfg.getServletContext(),refreshInterval);
			reportRefresher.loadTitles();	// force a load.
			reportRefresher.start();
			//*/

   			// Put a Set in the servlet context that holds the currently logged-in usernames.
			// Usernames will be added whenever there is a successful login and removed when the
			// user's session times out or is explicity logged out. This is used the prevent multiple
			// users logging in with the same user-id simlutaneously.
			// The addition is done in the loginXXX() functions and removal is doen from the
			// ServerManagement classes' code , which gets activated whenever a session invalidation happens.
			//cfg.getServletContext().setAttribute("setLoggedInUsers",new HashSet());
			cfg.getServletContext().setAttribute("mapIndustryWatchLoggedInUsers",new HashMap());
			System.out.println("IndustryWatchServlet inited");
		}catch(Exception e){
			e.printStackTrace();
			throw new UnavailableException("Problem in loading IndustryWatchServlet"); // servlet is not usable.
		}
	}

	/**
	 *
	 */
	public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
		doPost(request,response);
	}

	/**
	 *
	 */
	public void doPost(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
		try{
			System.out.println(">>>>>>>>>>>>>>>>");
			String mode = request.getParameter("mode");

			System.out.println(new java.util.Date().toLocaleString() + " : mode : " + mode);
			if(mode==null || mode.equals("")){
				dispatch(request,response,"IndustryWatchLogin.jsp",true);
				return;
			};

			if(mode.equals("loginForIndustryWatch")){
				handleLoginForIndustryWatch(request,response);
			}

			if(mode.equals("generateSummary")){
				handleGenerateSummary(request,response);
			}

			if(mode.equals("generateIndustryDetail")){
				handleGenerateIndustryDetail(request,response);
			}

			if(mode.equals("logoutFromIndustryWatch")){
				handleLogoutFromIndustryWatch(request,response);
			}
			if(mode.equals("generateAnalysisPDF")){
				handleGenerateAnalysisPDF(request,response);
			}
			if(mode.equals("licenceAgreement")){
				handleLicenceAgreement(request,response);
			}

			if(mode.equals("pdfReportDownload")){
            	handlePdfReportDownload(request,response);
			}

			if(mode.equals("pdfReportDownloadForTrialUser")){
				handlePdfReportDownloadForTrialUser(request,response);
			}

		}catch(Exception e){
			System.out.println("CAUGHT");
			e.printStackTrace();
			request.setAttribute("javax.servlet.jsp.jspException",e); //the exception implicit JSP object.
			dispatch(request,response,"/mr_errhandler.jsp",false);
		}finally{
			System.out.println("<<<<<<<<<<<<<<<");
		}
	}

	/**
	 *
	 */
	public void destroy(){
		System.out.println("IW Servlet going down");
		this.reportRefresher.interrupt();
	}

	/**
	 * Generates the PDF format summary data of the selected industry sectors.
	 *
	 * @param request
	 * @param response
	 * @throws ServletException
	 * @throws IOException
	 */
	protected void handleGenerateSummary(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
		// retrieve the industry segment input.
		String industrySector = request.getParameter("hidIndustrySector");

		byte[] data = IndustryWatchManager.getIndustryWatchSummaryPDFReport(industrySector);
		// send the PDF-formatted data in the response stream.
		response.setContentType("application/pdf");
		response.setHeader("Content-disposition","inline;filename=" + industrySector +".pdf");
		ServletOutputStream os = response.getOutputStream();
		os.write(data,0,data.length);
		os.close();
	}

	/**
	 * Generates the PDF format detail data of the selected industry sectors.
	 *
	 * @param request
	 * @param response
	 * @throws ServletException
	 * @throws IOException
	 */
	protected void handleGenerateIndustryDetail(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{

    	try{
			String fileName = request.getParameter("reportFileName");
			File file = new File(ApplicationResources.getInstance().getProperty("iwSummaryPDFStorageFolder"),fileName);
			byte[] data = new byte[(int)file.length()];
	    	FileInputStream fIn = new FileInputStream(file);

			// send the PDF-formatted data in the response stream.
			response.setContentType("application/pdf");
			response.setHeader("Content-disposition","inline;filename=industrySectorDetail.pdf");
			ServletOutputStream os = response.getOutputStream();

			int length = -1;
			while((length = fIn.read(data))!= -1){
				os.write(data,0,length);
			}
			os.close();

			fIn.close();
			file.delete();
		}catch(Exception e){
			e.printStackTrace();
		}
	}


	/**
	 * Generates the PDF format summary of the tab 3 contents of the IW Main Page.
	 *
	 * @param request
	 * @param response
	 * @throws ServletException
	 * @throws IOException
	 */
	protected void handleGenerateAnalysisPDF(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
    	try{
			String fileName = request.getParameter("pdfFileName");
			File file = new File(ApplicationResources.getInstance().getProperty("iwSummaryPDFStorageFolder"),fileName);
			byte[] data = new byte[(int)file.length()];
	    	FileInputStream fIn = new FileInputStream(file);

			// send the PDF-formatted data in the response stream.
			response.setContentType("application/pdf");
			response.setHeader("Content-disposition","inline;filename=industrySectorAnalysis.pdf");
			ServletOutputStream os = response.getOutputStream();

			int length = -1;
			while((length = fIn.read(data))!= -1){
				os.write(data,0,length);
			}
			os.close();

			fIn.close();
			file.delete();
		}catch(Exception e){
			e.printStackTrace();
		}

	}


	/**
	 * Logs in a IW user.
	 * 1. Checks for the existance of the the emailid in the table
	 * 2. Checks whether the expiry date of the login id.
	 * 	  If the validity of the user has finished, or about to finish the user is forwarded to the "re-registration" page.
	 *
	 * @param  request
	 * @param  response
	 * @throws ServletException
	 * @throws IOException
	 * @throws ParseException
	 */
	protected void handleLoginForIndustryWatch(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException,ParseException{
		String uid = request.getParameter("txtEmailAddress");
		String pwd = request.getParameter("txtPassword");

		String nextView=null;
		if(uid == null || uid.length() == 0){
			// insufficient information.
			request.setAttribute("error.login","Incorrect Details Entered");
			nextView = "/IndustryWatchLogin.jsp";
		}else{
			uid = uid.toLowerCase().trim();
			pwd = pwd.toLowerCase();

			// find out if someone is already logged in with this user-id.
			//System.out.println("login for " + uid + " " + ((Set)getServletContext().getAttribute("setLoggedInUsers")).contains(uid));
			System.out.println("login for " + uid + " " + ((Map)getServletContext().getAttribute("mapIndustryWatchLoggedInUsers")).containsValue(uid));
			if(((Map)getServletContext().getAttribute("mapIndustryWatchLoggedInUsers")).containsValue(uid)){
				// some one is already logged in.
				request.setAttribute("error.login","Some other user is already logged in with this Username");
				nextView = "/IndustryWatchLogin.jsp";
			}else{

				IWCustomerDetailsBean bean = IndustryWatchManager.loginForIndustryWatch(uid,pwd);
				if(bean==null){
					// login has failed
					request.setAttribute("error.login","Incorrect Details Entered");
					nextView = "/IndustryWatchLogin.jsp";
				}else{

					ApplicationResources resource = ApplicationResources.getInstance();
					int sessionTimeoutDuration = Integer.parseInt(resource.getProperty("iwSessionTimeoutDuration"));

					if(bean.isTrialIndicator()){
						// This is a demo-version user. Send him to the demo page.
						HttpSession session = request.getSession(true);
						session.setAttribute("IWusername",uid);
						session.setAttribute("IWUserDetails",bean);
						// session validity .
						session.setMaxInactiveInterval(sessionTimeoutDuration*60);
						nextView = "/iwMainPageDemoVersion.jsp";
						((Map)getServletContext().getAttribute("mapIndustryWatchLoggedInUsers")).put(session.getId(),uid);
					}else{
						// check for the validity of the user's subscripttion.
		    			Date subsEndDate = bean.getSubscriptionEndDate();

						long expiryDateMillis 	= 0;
						long currentDateMillis 	= System.currentTimeMillis();

						if(subsEndDate != null){
							// Move the date to the end second of the expiry date.
							// The original object is java.sql.Date, which cannot hold hh,mm,ss info.
							subsEndDate = new Date(subsEndDate.getTime());
							subsEndDate.setHours(23);
							subsEndDate.setMinutes(59);
							subsEndDate.setSeconds(59);
							expiryDateMillis 	= subsEndDate.getTime();
						}

						if(currentDateMillis < expiryDateMillis){
							//  subscription is still valid.
							HttpSession session = request.getSession(true);
							//session.setAttribute("username",bean.getEmailAddress());
							session.setAttribute("IWusername",uid);
							// session validity.
							session.setMaxInactiveInterval(sessionTimeoutDuration*60);

							//((Set)getServletContext().getAttribute("setLoggedInUsers")).add(uid);
							((Map)getServletContext().getAttribute("mapIndustryWatchLoggedInUsers")).put(session.getId(),uid);

		   					// check if the subscription is about to expire in the next n days(default n = 30).
							// For the 'C' type subscribers the cutoff days is 2 days.
		   					int cutoffDays = 0;
							if("C".equals(bean.getSubscriptionType())){
								cutoffDays = 2;
							}else{
								cutoffDays = Integer.parseInt(ApplicationResources.getInstance().getProperty("registration.expiry.days","30"));
							}

							long diff = expiryDateMillis - currentDateMillis;

							int days = (int)(diff/(1000*60*60*24));

							if(days < cutoffDays){
								// prompt the user to re-subscribe to the service before going forward.

								// format the date into a user-friendly format.(default is dd-MMM-yyyy)
				    			String dateFormat = ApplicationResources.getInstance().getProperty("display.dateformat","dd-MMM-yyyy");
				    			SimpleDateFormat df = new SimpleDateFormat(dateFormat);
		    					String strFormattedDate = df.format(subsEndDate);
								request.setAttribute("subsEndDate",strFormattedDate + " (in " + days + " days)");
								session.setAttribute("emailAddress",bean.getEmailAddress()); // used for auto-filling textfield in order-entry form
								session.setAttribute("IWUserDetails",bean);
								// fetch the previously ordered reports
								nextView ="/iwAskForReregistration.jsp";
							}else{
								session.setAttribute("IWUserDetails",bean);
								nextView= "/iwMainPage.jsp";
							}
						}else{
							// subscription has expired.
							request.setAttribute("subscription.expired",Boolean.TRUE);
							HttpSession session = request.getSession(true);
							session.setAttribute("emailAddress",bean.getEmailAddress()); // used for auto-filling textfield in order-entry form
							nextView = "/iwAskForReregistration.jsp";
						}
					}
				}
			}
		}
		dispatch(request,response,nextView,false);
	}

	/**
	 * Logs out the IW user.
	 */
	protected void handleLogoutFromIndustryWatch(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException,ParseException{
 		String nextView = "/IndustryWatchLogin.jsp";
        boolean redirect = false;
 		HttpSession session = request.getSession(false);
 		if(session != null){
	 		IWCustomerDetailsBean iwCDBean = (IWCustomerDetailsBean)session.getAttribute("IWUserDetails");
			session.invalidate();


	 		if(request.getParameter("nextPage") != null){
	 			// This is a subscription request from the demo version of IW.
	 			// Retrieve the corresponding registration info and
	 			// forward the user to the subscription page.
	 			// Additional logic - if the user id of the bean is 99999 (trial user), then don't
	 			// retrieve the details.
	 			if(iwCDBean.getCustomerDetailsId() != 99999){
				   	IWRegistrationBean iwRegBean = WebsiteEnquiriesManager.getIWOrder(iwCDBean.getEmailAddress());
				   	// put the first, last names ,company name & telephone nos.
				   	iwRegBean.setTxtFirstName(iwCDBean.getFirstName());
				   	iwRegBean.setTxtSurname(iwCDBean.getSurname());
				   	iwRegBean.setTxtCompanyName(iwCDBean.getCompanyName());
				   	iwRegBean.setTxtTelephone(iwCDBean.getTelephoneNumber());
			     	request.getSession(true).setAttribute("iwRegBean",iwRegBean);
				}
	 			nextView = "mr_reportSelection.jsp?view=IndustryWatch&type=ABC&iwv=C";
	 			redirect = true;
	 		}else{
	 		}
		}

		dispatch(request,response,nextView,redirect);
	}


	/**
	 * handles the response of the user to the licence agreement.
	 */
	protected void handleLicenceAgreement(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException,ParseException{
		String agreed = request.getParameter("agreed");
		Boolean bObj = Boolean.valueOf(agreed);
		if(bObj == Boolean.TRUE){
			// user has agreed to the licence agreement.
			// update the database records.
   			IWCustomerDetailsBean bean = (IWCustomerDetailsBean)request.getSession().getAttribute("IWUserDetails");
			IndustryWatchManager.updateIWCustomerLicenceAcceptance(bean.getEmailAddress());
   			bean.setLicenceAcceptanceDate(new java.util.Date());
			String nextView= "/iwMainPage.jsp";
			dispatch(request,response,nextView,false);
		}else{
			// user has refused to accept the agreement, so send him to the logout option.
			handleLogoutFromIndustryWatch(request,response);
		}
	}

	/**
	 * Provides the PDF downloads of Merlin reports.
	 */
	protected void handlePdfReportDownload(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException,ParseException{
		long start = System.currentTimeMillis();
		String downloadId = request.getParameter("downloadId");
		PDFReportFileBean pdfData = IndustryWatchManager.getPDFReportByUUID(downloadId);

		if(pdfData.getFileName() != null){
			response.setContentType("application/pdf");
			System.out.println("downloading " + pdfData.getFileName());
			//have to put the filename in double quotes otherwise the filename gets truncated to the first word of the file while downloading
			response.setHeader("Content-Disposition","attachment;filename=\"" + pdfData.getFileName() + "\"");
			response.setContentLength(pdfData.getFileData().length);
	  		response.getOutputStream().write(pdfData.getFileData());
	  		response.getOutputStream().close();
		}
  		System.out.println(System.currentTimeMillis() - start);
	}

	protected void handlePdfReportDownloadForTrialUser(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException,ParseException{
		int reportId = Integer.parseInt(request.getParameter("reportId"));
		System.out.println("handlePdfReportDownloadForTrialUser " + reportId);
		// make sure no one can misuse this facility. Check that the reportids are for the demo reports only.
		if(reportId != 379 && reportId != 301 && reportId != 682){	// Ford, Nissan, Volkswagen
			response.setContentType("text/html");
			response.getWriter().println("<h3>You are not authorized to download this report.</h3>");
			return;
		}

		PDFReportFileBean pdfData = IndustryWatchManager.getPDFReportByReportId(reportId);

		if(pdfData.getFileName() != null){
			response.setContentType("application/pdf");
			System.out.println("downloading " + pdfData.getFileName());
			//have to put the filename in double quotes otherwise the filename gets truncated to the first word of the file while downloading
			response.setHeader("Content-Disposition","attachment;filename=\"" + pdfData.getFileName() + "\"");
			response.setContentLength(pdfData.getFileData().length);
	  		response.getOutputStream().write(pdfData.getFileData());
	  		response.getOutputStream().close();
		}
	}

	/**
	 * Forwards/redirects the request to the requested resource.
	 */
	protected void dispatch(HttpServletRequest request,HttpServletResponse response,String page,boolean redirect) throws ServletException,IOException{
		if(redirect){
			System.out.println("redirecting to " + page);
			response.sendRedirect(page);
		}else{
			System.out.println("forwarding to " + page);
			RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(page);
			dispatcher.forward(request, response);
		}
	}


	/**
	 * Thread to continuously refresh the report titles stored in the servletcontext.
	 */
	class ReportRefresher extends Thread{
		ServletContext context;
		int refreshInterval;
		public boolean isServerActive = true;

		/**
		 *
		 */
		ReportRefresher(ServletContext context,int refreshInterval){
			this.context = context;
			this.refreshInterval = refreshInterval;
		}

		/**
		 *
		 */
		public void run(){
			try{
				while(isServerActive){
					loadTitles();
					System.out.println("Going to sleep for " + (refreshInterval * 60 * 1000) + " milliseconds");
    				sleep(refreshInterval * 60 * 1000);	// converted to milliseconds
    			}
    		}catch(InterruptedException ie){
    			// Thread has been interrupted. Ignore the error and stop the thread.
    			System.out.println("ReportRefresher Thread interrupted");
			}catch(Exception e){
				e.printStackTrace();
			}
		}

		/**
		 *
		 */
		public void loadTitles(){
			// cache the report titles, as they will be used repeatedly.
	  		// format a table of report titles
	  		List lstTitles = ReportTitlesManager.selectReportTitlesRecords(
	  				"REPORT_TITLES_ID between 1 and 26000 " +
	  				"and REPORT_TITLE <> 'AAA' " +
	  				"and coalesce(CUSTOMISED_INDICATOR,false) <> true",
	  			"REPORT_TITLE ASC");
	  		
			System.out.println(">>>>Total titles ::" + lstTitles.size());
	  		Map mapTitles = ReportTitlesManager.selectReportTitlesInSortedMap(lstTitles);

			String strTable = ReportTitlesManager.convertToTable(mapTitles);

			context.setAttribute("totalReportsAvailable", new Integer(lstTitles.size()));
			context.setAttribute("reportTitlesTable",strTable);


			System.out.println("Updated report data on " + new Date().toLocaleString());
		}
	}
}